package com.desaysv.mvi

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.desaysv.mvi.intent.IIntent
import com.desaysv.mvi.model.BaseViewModel
import com.desaysv.mvi.state.IState
import kotlinx.coroutines.*
import java.lang.reflect.ParameterizedType

/**
 * @author linzi
 * @date 2023-3-31
 */
abstract class MVIFragment<T : BaseViewModel<E, K>, K : IState, E : IIntent> : Fragment() {
    private val TAG = "MVIFragment"
    lateinit var viewModel: T

    /**
     * state消费
     */
    abstract fun consumerStates(state: K)


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        initModel()
        initConsumer()

    }

    /**
     * 调用Model发送intent
     */
    fun send(intent: E) {
        doLaunch {
            viewModel.currentIntent.send(intent)
        }
    }

    /**
     * 初始化state的消费者
     */
    private fun initConsumer() {
        doLaunch {
            viewModel.currentState.collect {
                doMain{
                    consumerStates(it)
                }
            }
        }
    }

    /**
     * 直接从泛型类里面读取Model进行初始化
     */
    private fun initModel() {
        //识别泛型类
        val tc = (this.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments
        var claz: Class<T>? = null
        for (type in tc) {
            val clzTemp = type as Class<T>
            if (clzTemp.superclass === BaseViewModel::class.java) {
                claz = clzTemp
            }
        }
        if (claz == null) {
            throw RuntimeException("need BaseViewModel impl")
        }
        viewModel = viewModels(claz)
    }

    /**
     * 初始化model
     */
    private fun <T : ViewModel> viewModels(cla: Class<T>): T {
        val provider =
            (ViewModelProvider.AndroidViewModelFactory(requireActivity().application) as ViewModelProvider.Factory?)?.let {
                ViewModelProvider(
                    this,
                    it
                )
            }
        return provider!![cla]
    }

    @OptIn(DelicateCoroutinesApi::class)
    fun doLaunch(block: suspend CoroutineScope.() -> Unit) {
        GlobalScope.launch {
            block.invoke(this)
        }
    }

    @OptIn(DelicateCoroutinesApi::class)
    fun doMain(block: suspend CoroutineScope.() -> Unit) {
        GlobalScope.launch (Dispatchers.Main){
            block.invoke(this)
        }
    }
}